﻿using DesertKharonIrcPlugin.GdiPlusRendering;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace DesertKharonIrcPlugin.Emotes
{
    public class Emote : IDisposable
    {
        #region Background Worker Classes
        private class Arguments
        {
            public Uri Uri { get; set; }
            public string CacheFolderPath { get; set; }
            public TimeSpan TimeToCacheUpdate { get; set; }
            public float Scale { get; set; }
        }

        private class Result
        {
            public Bitmap Bitmap { get; set; }
            public DateTime CreationTime { get; set; }
        }
        #endregion

        private object _lock = new object();

        private List<string> _keyList;

        private DateTime _lastUpdated;
        private Uri _uri;
        private string _cacheFolderPath;
        private float _scale = 1f;
        private TimeSpan _timeToCacheUpdate = TimeSpan.FromDays(7d);
        
        private Bitmap _bitmap;
        private bool _isLoaded = false;
        private bool _isLoading = false;

        private AbortableBackgroundWorker _backgroundWorker;
        private LoadingAnimation _loadingAnimation;

        public LoadingAnimation LoadingAnimation
        {
            get { return _loadingAnimation; }
            set { _loadingAnimation = value; }
        }

        public List<string> KeyList
        {
            get { return _keyList; }
        }

        public Bitmap Bitmap
        {
            get { return _bitmap; }
        }

        public bool IsLoaded
        {
            get { return _isLoaded; }
        }

        public bool IsLoading
        {
            get { return _isLoading; }
        }

        public Emote(List<string> keyList, Uri uri, string cacheFolderPath, float scale)
        {
            _keyList = keyList;
            _uri = uri;
            _cacheFolderPath = cacheFolderPath;
            _scale = scale;
        }

        public void Load()
        {
            if (_isLoading == false)
            {
                _isLoaded = false;
                _isLoading = true;

                _backgroundWorker = new AbortableBackgroundWorker();
                _backgroundWorker.RunWorkerCompleted += LoadEmoteCompleted;
                _backgroundWorker.DoWork += LoadEmoteWorker;
                _backgroundWorker.RunWorkerAsync(new Arguments 
                { 
                    Uri = _uri,
                    CacheFolderPath = _cacheFolderPath,
                    TimeToCacheUpdate = _timeToCacheUpdate,
                    Scale = _scale
                });
            }
        }

        private void LoadEmoteWorker(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            Arguments arguments = e.Argument as Arguments;
            if (arguments != null)
            {
                bool downloadFile = false;

                string filepath = Path.Combine(arguments.CacheFolderPath, arguments.Uri.Segments.Last());
                if (File.Exists(filepath))
                {
                    if (DateTime.Now.Subtract(File.GetCreationTime(filepath)) > arguments.TimeToCacheUpdate)
                        downloadFile = true;
                }
                else
                    downloadFile = true;

                if (_backgroundWorker.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                if (downloadFile)
                { 
                    using (WebClient webClient = new WebClient())
                    {
                        webClient.DownloadFile(arguments.Uri, filepath);
                    }
                }

                if (_backgroundWorker.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                if (File.Exists(filepath))
                {
                    using (Bitmap bitmap = new Bitmap(Image.FromFile(filepath)))
                    {
                        e.Result = new Result
                        {
                            CreationTime = File.GetCreationTime(filepath),
                            Bitmap = bitmap.Scale(arguments.Scale)
                        };
                    }
                }
                else
                    throw new Exception(String.Format("Could not be loaded: {0}", arguments.Uri));
            }
        }

        private void LoadEmoteCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            if (!e.Cancelled && e.Error == null)
            {
                Result result = e.Result as Result;
                if (result != null)
                {
                    lock (_lock)
                    {
                        if (_bitmap != null)
                            _bitmap.Dispose();

                        _bitmap = result.Bitmap;
                    }

                    _isLoaded = true;
                }
            }

            _isLoading = false;
        }

        public void Dispose()
        {
            if (_bitmap != null)
            {
                _bitmap.Dispose();
                _bitmap = null;
            }

            if (_backgroundWorker != null)
            {
                if (_backgroundWorker.IsBusy)
                    _backgroundWorker.CancelAsync();

                while (_backgroundWorker.IsBusy) { Application.DoEvents(); }

                _backgroundWorker.RunWorkerCompleted -= LoadEmoteCompleted;
                _backgroundWorker.DoWork -= LoadEmoteWorker;

                _backgroundWorker.Dispose();
                _backgroundWorker = null;
            }
        }
    }
}
